﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;

namespace GMusicBox.Core
{
    public class BassEngine : IDisposable
    {
        public BassEngine(IntPtr Handle)
        {
            if (!init) MixerStreamHandle = InitBassEngine(Handle);

            //获取当前默认输出设备
            BASS_DEVICEINFO[] bd = Bass.BASS_GetDeviceInfos();
            for (int i = 0; i < bd.Length; i++)
                if (bd[i].IsDefault)
                { lastDefaultDevice = i; break; }
            
            //设置时间控件
            refreshState.Interval = 1;
            refreshState.Tick += new EventHandler(refreshState_Tick);
            refreshState.Enabled = true;
        }

        public event EventHandler PlayStateChanged;             //当播放状态更改时发生
        public event EventHandler Tick;                         //兼当Interval为1的时间控件

        static bool  init = false;                              //当前Bass是否已加载
               Timer refreshState = new Timer();                //时间控件
               bool  isReloading = false;                       //是否正在切换输出设备

               bool  firstPlay = false;

        bool          muted = false;
        int           lastDefaultDevice = -1;                   //最后的输出设备
        string        lastOpenedFile = "";                      //最后打开的文件
        BassPlayState lastPlayState = BassPlayState.Stopped;    //最后的播放状态
        TimeSpan      lastPosition = TimeSpan.FromSeconds(0);   //最后的播放位置
        string        lastSongName = "";                        //最后设置的歌曲名
        double        lastVolume = 1;                           //最后设置的音乐
        IntPtr        windowHandle = IntPtr.Zero;               //初始化时提供的窗口句柄

        int[] eq = new int[10];                                 //10个EQ设置的句柄
        bool  eqEnabled = false;                                //是否开启EQ
        int[] eqValueCache = new int[10];                       //最后的EQ设置

        int  reverb = 0;                                        //混响句柄
        bool reverbEnabled = false;                             //是否开启混响
        int  reverbTimeCache = 1000;                            //最后的混响设置

        int     preamp = 0;                                     //声音放大器句柄
        DSPPROC preampDSP = null;                               //声音放大器处理函数
        bool    preampEnabled = false;                          //是否开启声音放大器
        float   preampGain = 1f;                                //声音放大器效果

        List<DSP> dsp = new List<DSP>();                        //所有音效插件参数

        public static int InitBassEngine(IntPtr Handle)
        {
            //初始化Bass引擎
            //BassNet.Registration("hjkcai@hotmail.com", "2X31192918312422");
            try
            {
                if (HiWord(Bass.BASS_GetVersion()) != Bass.BASSVERSION) throw new Exception();
                if (!(init = Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero))) throw new Exception();
                else
                {
                    BassWaDsp.BASS_WADSP_Init(Handle);
                    return BassMix.BASS_Mixer_StreamCreate(44100, 2, BASSFlag.BASS_SAMPLE_FLOAT);
                }
            }
            catch
            {
                MessageBox.Show("Bass初始化失败，请尝试结束未关闭的谷歌音乐盒进程后再试。\n若重新启动谷歌音乐盒后依然出现此问题请联系客服。", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                Application.Exit();
                return 0;
            }
        }

        public static int[][] DefaultEqualizers
        {
            //EQ预设
            get
            {
                return new int[][] { 
                       new int[] {  4,  2,  0, -3, -6, -6, -3,  0,  3,  5 },    //推荐
                       new int[] {  3,  1,  0, -2, -4, -4, -2,  0,  1,  2 },    //流行音乐
                       new int[] { -2,  0,  2,  4, -2, -2,  0,  0,  4,  4 },    //摇滚
                       new int[] {  3,  6,  8,  3, -2,  0,  4,  7,  9, 10 },    //蓝调
                       new int[] { -6,  0,  0,  0,  0,  0,  4,  0,  4,  0 },    //金属乐
                       new int[] {  5,  4,  2,  0, -2,  0,  3,  6,  7,  8 },    //慢歌
                       new int[] { -2,  3,  4,  1, -2, -2,  0,  0,  4,  4 },    //舞曲
                       new int[] { -2,  3,  5,  6,  4, -1, -2, -2, -2, -2 },    //清新
                       new int[] {  9,  8,  8,  8,  6, -2, -8,-10,-10,-10 },    //重低音
                       new int[] { -8, -8, -8, -3,  2,  7, 12, 12, 12, 12 },    //重高音
                       new int[] { -6,  1,  4, -2, -2, -4,  0,  0,  6,  6 },    //电子乐
                       new int[] { -2,  0,  0,  2,  2,  0,  0,  0,  4,  4 },    //乡村音乐
                       new int[] {  0,  0,  0,  4,  4,  4,  0,  2,  3,  4 },    //爵士乐
                       new int[] {  0,  8,  8,  4,  0,  0,  0,  0,  2,  2 },    //古典乐
                       new int[] { -2,  0,  2,  1,  0,  0,  0,  0, -2, -4 },    //布鲁斯
                       new int[] { -1,  0,  0, -3,  0,  3,  4,  0,  3,  4 },    //瑞格乐
                       new int[] { -4,  0,  2,  1,  0,  0,  0,  0, -4, -6 },    //怀旧音乐
                       new int[] {  0,  0,  0,  4,  5,  3,  6,  3,  0,  0 },    //歌剧
                       new int[] { -4,  0,  2,  1,  0,  0,  0,  0, -4, -6 },    //语音
                       new int[] {  8,  8,  4,  4,  0, -4, -4, -4,  0,  0 }     //混响
                };
            }
        }

        public TimeSpan Duration
        {
            //总时间
            get { return TimeSpan.FromSeconds(Bass.BASS_ChannelBytes2Seconds(Handle, Bass.BASS_ChannelGetLength(Handle, BASSMode.BASS_POS_BYTES))); }
        }

        public int[] EqualizerCenters
        {
            //EQ的中心
            get { return new int[] { 31, 62, 125, 250, 500, 1000, 2000, 4000, 8000, 16000 }; }
        }

        public double Equilibrium
        {
            //声道平衡
            get
            {
                float value = 0;
                Bass.BASS_ChannelGetAttribute(MixerStreamHandle, BASSAttribute.BASS_ATTRIB_PAN, ref value);
                return value;
            }
            set { Bass.BASS_ChannelSetAttribute(MixerStreamHandle, BASSAttribute.BASS_ATTRIB_PAN, (float)value); }
        }

        public string FileName
        {
            get { return lastOpenedFile; }
            set { Load(value, ""); }
        }

        public float[] FFTData
        {
            //动态频谱信息
            get
            {
                if (PlayState != BassPlayState.Playing) return new float[512];
                float[] data = new float[512];
                Bass.BASS_ChannelGetData(Handle, data, (int)BASSData.BASS_DATA_FFT1024);
                return data;
            }
        }

        //歌曲句柄
        public int Handle { get; private set; }

        public bool IsEqualizerEnabled
        {
            //是否开启EQ
            get { return eqEnabled; }
            set
            {
                if (value == eqEnabled) return;
                else
                {
                    eqEnabled = value;
                    if (eqEnabled)
                    {
                        for (int i = 0; i < 10; i++)
                        {
                            eq[i] = Bass.BASS_ChannelSetFX(MixerStreamHandle, BASSFXType.BASS_FX_DX8_PARAMEQ, 1);
                            SetEqualizer(i, eqValueCache[i]);
                        }
                    }
                    else
                        for (int i = 0; i < 10; i++)
                        {
                            Bass.BASS_ChannelRemoveFX(MixerStreamHandle, eq[i]);
                            eq[i] = 0;
                        }
                }
            }
        }

        public bool IsPreampEnabled
        {
            //是否开启声音放大器
            get { return preampEnabled; }
            set
            {
                if (value == preampEnabled) return;
                else
                {
                    preampEnabled = value;
                    if (preampEnabled)
                    {
                        preampDSP = new DSPPROC(PreampDSPGain);
                        preamp = Bass.BASS_ChannelSetDSP(MixerStreamHandle, preampDSP, IntPtr.Zero, 2);
                    }
                    else
                    {
                        Bass.BASS_ChannelRemoveDSP(MixerStreamHandle, preamp);
                        preamp = 0;
                        preampDSP = null;
                    }
                }
            }
        }

        public bool IsReverbEnabled
        {
            //是否开启混响
            get { return reverbEnabled; }
            set
            {
                if (value == reverbEnabled) return;
                else
                {
                    reverbEnabled = value;
                    if (reverbEnabled)
                    {
                        reverb = Bass.BASS_ChannelSetFX(MixerStreamHandle, BASSFXType.BASS_FX_DX8_REVERB, 1);
                        SetReverbTime(reverbTimeCache);
                    }
                    else
                    {
                        Bass.BASS_ChannelRemoveFX(MixerStreamHandle, reverb);
                        //这里可能是Bass.Net的bug，移出混响后需要切换EQ的开启状态才能完整移除
                        IsEqualizerEnabled = !IsEqualizerEnabled;
                        IsEqualizerEnabled = !IsEqualizerEnabled;
                    }
                }
            }
        }

        //是否启用Tick事件
        public bool IsTimerEnabled { get; set; }

        public int LeftChannelLevel
        {
            //左声道峰值
            get { return HiWord(Bass.BASS_ChannelGetLevel(MixerStreamHandle)); }
        }

        //DSP所用的流句柄
        public int MixerStreamHandle { get; private set; }

        public bool Mute
        {
            get { return muted; }
            set
            {
                if (value) Volume = 0;
                else Volume = lastVolume == 0 ? 1 : lastVolume;
                muted = value;
            }
        }

        public BassPlayState PlayState
        {
            //当前播放状态
            get
            {
                BassPlayState bps = (BassPlayState)((int)Bass.BASS_ChannelIsActive(MixerStreamHandle));
                if (Position == Duration) bps = BassPlayState.MusicEnded;
                return bps;
            }
        }

        public TimeSpan Position
        {
            //当前播放进度
            get { return TimeSpan.FromSeconds(Bass.BASS_ChannelBytes2Seconds(Handle, Bass.BASS_ChannelGetPosition(Handle))); }
            set { Bass.BASS_ChannelSetPosition(Handle, value.TotalSeconds); }
        }

        public int RightChannelLevel
        {
            //右声道峰值
            get { return LoWord(Bass.BASS_ChannelGetLevel(MixerStreamHandle)); }
        }

        public double Volume
        {
            //音量
            get
            {
                float value = 0;
                Bass.BASS_ChannelGetAttribute(MixerStreamHandle, BASSAttribute.BASS_ATTRIB_VOL, ref value);
                if (muted) return lastVolume;
                if (!muted && value == 0) return lastVolume;
                return value;
            }
            set
            {
                if (!muted || (muted && value != 0))
                {
                    Bass.BASS_ChannelSetAttribute(MixerStreamHandle, BASSAttribute.BASS_ATTRIB_VOL, (float)value);
                    muted = value == 0;
                }
                lastVolume = value;
            }
        }

        public void AddDSP(string fileName)
        {
            //添加音效插件
            DSP d = new DSP();
            d.FileName = fileName;
            d.Handle = BassWaDsp.BASS_WADSP_Load(d.FileName, 5, 5, 100, 100, null);
            d.Description = BassWaDsp.BASS_WADSP_GetName(d.Handle);
            dsp.Add(d);

            LoadDSP(dsp.Count - 1);
        }

        public void CloseFile()
        {
            //关闭文件
            Stop();
            refreshState.Enabled = false;
            lastPlayState = BassPlayState.MusicEnded;
            BassMix.BASS_Mixer_ChannelRemove(Handle);
            Bass.BASS_StreamFree(Handle);
            Handle = 0;
            refreshState.Enabled = true;
            refreshState.Start();
        }

        public void ConfigDSP(int index)
        {
            //打开音效插件配置窗口
            BassWaDsp.BASS_WADSP_Config(dsp[index].Handle);
        }

        public void Dispose()
        {
            Free();
        }

        public int FindDSP(string fileName)
        {
            for (int i = 0; i < dsp.Count; i++)
                if (dsp[i].FileName.ToLower().IndexOf(fileName.ToLower()) != -1) return i;
            return -1;
        }

        public void Free()
        {
            //卸载Bass引擎
            CloseFile();
            Bass.BASS_StreamFree(MixerStreamHandle);

            try
            {
                BassWaDsp.BASS_WADSP_Free();
                System.Threading.Thread.Sleep(100);
            }
            catch { }

            Bass.BASS_Free();
        }

        private static int HiWord(int i)
        {
            return (short)(i >> 16);
        }

        public void Load(string fileName, string songName)
        {
            //打开文件
            CloseFile();

            Handle = Bass.BASS_StreamCreateFile(fileName, 0, 0, BASSFlag.BASS_SAMPLE_FX | BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_DECODE);
            Position = TimeSpan.FromSeconds(0);

            for (int i = 0; i < dsp.Count; i++)
            {
                BassWaDsp.BASS_WADSP_SetSongTitle(dsp[i].Handle, songName);
                BassWaDsp.BASS_WADSP_SetFileName(dsp[i].Handle, lastOpenedFile);
            }

            BassMix.BASS_Mixer_StreamAddChannel(MixerStreamHandle, Handle, BASSFlag.BASS_MIXER_DOWNMIX);
            lastOpenedFile = fileName;
            lastSongName = songName;
            firstPlay = true;
        }

        private void LoadDSP(int index)
        {
            //加载音效插件
            DSP d = dsp[index];
            BassWaDsp.BASS_WADSP_Start(d.Handle, 0, MixerStreamHandle);
            BassWaDsp.BASS_WADSP_ChannelSetDSP(d.Handle, MixerStreamHandle, 1);
            System.Threading.Thread.Sleep(100);
        }

        private static int LoWord(int i)
        {
            return (short)(i & 0xFFFF);
        }

        private void OnPlayStateChanged()
        {
            //触发PlayStateChanged事件
            if (isReloading) return;
            if (PlayStateChanged != null) PlayStateChanged(this, new EventArgs());
        }

        public void Play()
        {
            //播放
            if (firstPlay)
            {
                double vol = Volume;
                Volume = 0;
                Bass.BASS_ChannelPlay(MixerStreamHandle, false);
                System.Threading.Thread.Sleep(500);
                Volume = vol;
            }
            else Bass.BASS_ChannelPlay(MixerStreamHandle, false);
        }

        public void Pause()
        {
            //暂停
            Bass.BASS_ChannelPause(MixerStreamHandle);
        }
         
        private unsafe void PreampDSPGain(int handle, int channel, IntPtr buffer, int length, IntPtr user)
        {
            if (preampGain == 1f || length == 0 || buffer == IntPtr.Zero)
                return;

            int l4 = length / 4;

            float* data = (float*)buffer;
            for (int a = 0; a < l4; a++)
                data[a] = data[a] * preampGain;
        }

        private void refreshState_Tick(object sender, EventArgs e)
        {
            //每1ms刷新一次设备和播放状态，并执行Tick事件
            BASS_DEVICEINFO[] bd = Bass.BASS_GetDeviceInfos();
            for (int i = 0; i < bd.Length; i++)
            {
                if (bd[i].IsDefault && lastDefaultDevice != i)
                {
                    //当默认输出设备更改时重新加载Bass引擎以更换输出设备
                    bool play = lastPlayState == BassPlayState.Playing;
                    lastDefaultDevice = i;
                    lastPosition = Position;
                    isReloading = true;

                    Free();
                    MixerStreamHandle = InitBassEngine(windowHandle);
                    if (File.Exists(lastOpenedFile))
                    {
                        DSP[] dsps = dsp.ToArray();
                        dsp.Clear();
                        foreach (DSP item in dsps) AddDSP(item.FileName);

                        Load(lastOpenedFile, lastSongName);
                        if (play)
                        {
                            Play();
                            Position = lastPosition;
                        }
                        lastPlayState = PlayState;
                    }

                    isReloading = false;
                    break;
                }
            }

            if (lastPlayState != PlayState)
            {
                lastPlayState = PlayState;
                OnPlayStateChanged();
            }

            if (IsTimerEnabled && Tick != null) Tick(this, new EventArgs());
        }

        public void RemoveDSP(int index)
        {
            //移出音效插件
            BassWaDsp.BASS_WADSP_FreeDSP(dsp[index].Handle);
            dsp.RemoveAt(index);
        }

        public void ReStart()
        {
            //从头开始播放
            Bass.BASS_ChannelPlay(MixerStreamHandle, true);
        }

        public void SetDefaultEqualizer(int index)
        {
            //设置预设的EQ
            for (int i = 0; i < 10; i++)
                SetEqualizer(i, DefaultEqualizers[index][i]);
        }

        public void SetEqualizer(int index, int gain)
        {
            //设置EQ效果强度
            BASS_DX8_PARAMEQ eqParam = new BASS_DX8_PARAMEQ();
            eqParam.fBandwidth = 18;
            eqParam.fCenter = EqualizerCenters[index];
            eqParam.fGain = gain;
            eqValueCache[index] = gain;
            Bass.BASS_FXSetParameters(eq[index], eqParam);
        }

        public void SetPreampGain(int gain)
        {
            //设置声音放大器效果强度
            preampGain = (float)Math.Pow(20d, gain / 12d);
        }

        public void SetReverbTime(int time)
        {
            //设置混响效果强度
            BASS_DX8_REVERB reverbParam = new BASS_DX8_REVERB();
            reverbParam.fReverbTime = time;
            reverbTimeCache = time;
            Bass.BASS_FXSetParameters(reverb, reverbParam);
        }

        public void Stop()
        {
            //停止播放
            Bass.BASS_ChannelStop(MixerStreamHandle);
        }
    }

    public class DSP
    {
        public string Description { get; set; }
        public string FileName { get; set; }
        public int Handle { get; set; }
    }

    public enum BassPlayState
    {
        Stopped,
        Playing,
        Stalled,
        Paused,
        MusicEnded
    }
}
